package com.vmj.instructions.references;

import com.vmj.heap.VMObject;
import com.vmj.instructions.base.NoOperandsInstruction;
import com.vmj.natives.java.lang.StackTraceElement;
import com.vmj.rtda.Frame;
import com.vmj.rtda.OperandStack;
import com.vmj.rtda.Thread;

/**
 * Created by yangshoujun on 2017/9/23 上午11:52.
 */
public class Athrow extends NoOperandsInstruction {

    @Override
    public void execute(Frame frame) {
        VMObject exObj = frame.getOperandStack().popRef();
        if(exObj == null) {
            throw new RuntimeException("java.lang.NullException");
        }

        frame.setHasHappenException(true);
        Thread thread = frame.getThread();
        boolean found = findAndGotoExceptionHandler(thread, exObj);
        if(!found) {
            handleUncaughtException(thread, exObj);
        }
    }

    /**
     * 查找异常处理handler
     *
     * @param thread
     * @param exObj
     * @return
     */
    private boolean findAndGotoExceptionHandler(Thread thread, VMObject exObj) {
        for(;;) {
            Frame frame = thread.getCurrentFrame();
            int pc = frame.getNextPc() - 1;
            // handlerPc出现异常后，跳转到catch代码块pc的开始位置
            int handlerPc = frame.getMethod().findExceptionHandler(exObj.getClazz(), pc);
            if(handlerPc > 0) {
                OperandStack stack = frame.getOperandStack();
                stack.clear();
                stack.pushRef(exObj);
                frame.setNextPc(handlerPc);
                return true;
            }
            thread.popFrame();
            if(thread.isStackEmpty()) {
                break;
            }
        }

        return false;
    }

    private void handleUncaughtException(Thread thread, VMObject exObj) {
        thread.clearStack();;
        VMObject msgObj = exObj.getRefVar("detailMessage", "Ljava/lang/String;");
        String msg = null;
        if(msgObj != null) {
            msgObj.getRefString();
        }

        System.out.println(exObj.getClazz().getJavaName() + ": " + msg);
        StackTraceElement[] elements = (StackTraceElement[]) exObj.getExtra();
        for(StackTraceElement element : elements) {
            System.out.println("\tat " + element.getClassName() + "." + element.getMethodName() + "(" + + element.getLineNumber() + ")");
        }
        System.exit(-1);
    }
}
